home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Magnum One
/
Magnum One (Mid-American Digital) (Disc Manufacturing).iso
/
d16
/
pchart.arc
/
CHARTS.PAS
next >
Wrap
Pascal/Delphi Source File
|
1991-05-05
|
15KB
|
597 lines
{**************************************************}
{ This unit defines the chart types used in the }
{ Windows charting program PCHART.PAS. }
{ Zack Urlocker }
{ 05/02/91 }
{ }
{ Five types are defined: }
{ TChart: formal type for inheritance }
{ THBarChart: horizontal bar chart }
{ TVBarChart: vertical bar chart }
{ TV3DBarChart: vertical 3D bar chart }
{ TPieChart: pie chart }
{ all types have a common protocol that includes }
{ drawing, rescaling and stream storage }
{**************************************************}
unit Charts;
{$IFDEF Final} { Remove debug code for final version}
{$D-,I-,L-,R-,S-}
{$ELSE}
{$D+,I+,L+,R+,S+}
{$ENDIF}
interface
uses WObjects, Dicts, WinTypes, WinProcs, Strings, StdDlgs, WinDOS;
type
{ Abstract type provides inheritance for other chart types }
PChart = ^TChart;
TChart = object(TObject)
{ Object fields }
Name : PChar; { title string }
Scale : TPoint; { scaling factor }
Area : TPoint; { size of the chart }
Lead : TPoint; { lead before edges }
Space : Integer; { space between items }
Items : PDict; { key->value pairs }
{ Functions and procedures }
constructor Init; { so that inheritance works }
destructor Done; virtual;{ to clean up memory }
procedure Draw(DC : HDC); virtual;
procedure DrawTitle(DC : HDC); virtual;
procedure DrawLabels(DC : HDC); virtual;
procedure DrawData(DC : HDC); virtual;
procedure ReScale; virtual;
procedure AdjustScale(max : Integer); virtual;
function getItem(x, y : integer) : PAssoc; virtual;
constructor Load(var S: TStream);
procedure Store(var S: TStream);
procedure add(Key : PChar; Value : Integer);
procedure remove(Key : PChar);
procedure ResetLead; virtual;
procedure ResetSpace; virtual;
end; { Chart }
PHBarChart = ^THBarChart;
THBarChart = object(TChart) { Horizontal bars }
procedure DrawLabels(DC : HDC); virtual;
procedure DrawData(DC : HDC); virtual;
procedure AdjustScale(max : Integer); virtual;
function getItem(x, y : integer) : PAssoc; virtual;
procedure ResetLead; virtual;
end; { THBarChart }
PVBarChart = ^TVBarChart;
TVBarChart = object(TChart) { Vertical bars }
procedure DrawLabels(DC : HDC); virtual;
procedure DrawData(DC : HDC); virtual;
procedure AdjustScale(max : Integer); virtual;
function getItem(x, y : integer) : PAssoc; virtual;
procedure ResetSpace; virtual;
procedure ResetLead; virtual;
end; { TVBarChart }
PV3DBarChart = ^TV3DBarChart; { Vertical 3D bars }
TV3DBarChart = object(TVBarChart)
procedure DrawData(DC : HDC); virtual;
end; { V3DBarChart }
PPieChart = ^TPieChart;
TPieChart = object(TChart) { Pie charts }
procedure DrawLabels(DC : HDC); virtual;
procedure DrawData(DC : HDC); virtual;
procedure AdjustScale(max : Integer); virtual;
function getItem(x, y : integer) : PAssoc; virtual;
procedure ResetSpace; virtual;
end; { TPieChart }
implementation
const
Black = $000000; { Windows color constants }
White = $FFFFFF;
Blue = $FF0000;
Green = $00FF00;
Red = $0000FF;
{ ********* Chart ********* }
constructor TChart.Init;
begin
GetMem(Name, 255);
Scale.x := 0;
Scale.y := 0;
Area.x := 0;
Area.y := 0;
ResetLead;
ResetSpace;
new(Items, init(10,5));
end;
{ Dispose of the chart by deallocating memory. }
destructor TChart.Done;
begin
StrDispose(Name);
Items^.Done;
end;
{ Draw a chart in the area }
procedure TChart.Draw(DC : HDC);
var s : array[0..16] of char;
begin
if Name <> nil then
DrawTitle(DC);
if items^.size > 0 then
begin
DrawLabels(DC);
DrawData(DC);
end
else
begin
strPCopy(S, '(Empty chart)');
TextOut(DC, 1, 2, s, strLen(s));
end;
end;
{ Draw the title, centered in a custom font}
procedure TChart.DrawTitle(DC : HDC);
var FontInfo: TLogFont;
oldFont, newFont : HFont;
x : Integer;
begin
{ set the font }
with FontInfo do
begin
lfHeight := 30;
lfWidth := 0;
lfWeight := 700;
lfItalic := 0;
lfUnderLine := 0;
lfStrikeOut := 0;
lfQuality := Proof_Quality;
strPcopy(lfFaceName, 'Tms Rmn');
end;
newFont := createFontIndirect(FontInfo);
OldFont := SelectObject(DC, newFont);
x := area.x div 2 - strLen(Name) * 10;
TextOut(DC, x, 1, Name, strLen(Name));
{ Reset the font when done }
selectObject(DC, oldFont);
DeleteObject(newFont);
end;
{ Force the chart to adjust its scale }
procedure TChart.ReScale;
var Max : Integer;
begin
Max := Items^.MaxValue;
If Max > 0 then
begin
resetLead;
resetSpace;
adjustScale(Max);
end;
end;
{ Abstract methods that must be implemented in descendant classes. }
procedure TChart.DrawData(DC : HDC);
begin
abstract;
end;
procedure TChart.DrawLabels(DC : HDC);
begin
abstract;
end;
procedure TChart.AdjustScale(max:Integer);
begin
abstract;
end;
function TChart.getItem(x, y : integer) : PAssoc;
begin
abstract;
end;
{ File and stream I/O methods }
constructor TChart.Load(var S:TStream);
{ Load a chart from a stream. Must be read in same order written. }
begin
Name := S.StrRead;
Items := PDict(S.Get);
end;
procedure TChart.Store(var S:TStream);
{ Store a chart onto a stream. Not all object fields are stored.
For example, scale, area, lead, space are set properly when
you rescale. Must be read in the exact same order. }
begin
S.StrWrite(Name);
S.Put(Items);
end;
{ Miscelaneous access methods }
procedure TChart.add(Key : PChar; Value : Integer);
begin
Items^.update(Key, Value);
end;
procedure TChart.remove(Key : PChar);
begin
Items^.remove(Key);
end;
procedure TChart.ResetLead;
begin
Lead.x := 10;
Lead.y := 30;
end;
procedure TChart.ResetSpace;
begin
Space := 10;
end;
{ ********* THBarChart ********* }
{ Draw labels with a stock font }
procedure THBarChart.DrawLabels(DC : HDC);
var I, x, y : Integer;
str : PChar;
procedure DrawLabel(Item : PAssoc); far;
begin
y := Lead.y + i*(Scale.y + space);
str := Item^.key;
TextOut(DC, x, y, str, strLen(str));
inc(i);
end;
begin
x := 1;
i := 0;
selectObject(DC, getStockObject(ansi_fixed_font));
Items^.ForEach(@DrawLabel);
selectObject(DC, getStockObject(system_font));
end;
{ Draw the bars in the chart }
procedure THBarChart.DrawData(DC : HDC);
var I, x, y : Integer;
procedure DrawItem(Item : PAssoc); far;
begin
y := Lead.y + i*(Scale.y + space);
Rectangle(DC, x, y, round(x+Item^.value*scale.x), y+scale.y);
inc(i);
end;
begin
x := lead.x;
i := 0;
SelectObject(DC, CreateSolidBrush(Blue));
Items^.ForEach(@DrawItem);
DeleteObject(SelectObject(DC, GetStockObject(Black_Brush)));
end;
{ Adjust the scale horizontally }
procedure THBarChart.AdjustScale(max : Integer);
begin
scale.x := (area.x - 2 * lead.x) div max;
scale.y := 25;
end;
{ Return item found at location x, y }
function THBarChart.getItem(x, y : integer) : PAssoc;
var index : Integer;
begin
index := trunc((y - lead.y)/ (scale.y + space));
if index < Items^.size then
getItem := Items^.at(index)
else
getItem := nil;
end;
{ Reset the lead for this type of chart }
procedure THBarChart.resetLead;
begin
lead.x := 60;
lead.y := 30;
end;
{ ********* TVBarChart ********* }
{ Draw labels in color font }
procedure TVBarChart.DrawLabels(DC : HDC);
var I, x, y : Integer;
str : PChar;
procedure DrawLabel(Item : PAssoc); far;
begin
x := i*(Scale.x+space) + lead.x;
str := Item^.key;
TextOut(DC, x, y, str, strLen(str));
inc(i);
end;
begin
i := 0;
y := area.y - lead.y+1;
setTextColor(DC, Blue);
Items^.ForEach(@DrawLabel);
setTextColor(DC, Black);
end;
{ Draw the bars in the chart }
procedure TVBarChart.DrawData(DC : HDC);
var I, x, y : Integer;
procedure DrawItem(Item : PAssoc); far;
begin
x := Lead.x + i*(Scale.x + space);
Rectangle(DC, x+Scale.x, area.y - lead.y, x,
round(area.y-lead.y-Item^.value*scale.y));
inc(i);
end;
begin
i := 0;
SelectObject(DC, CreateSolidBrush(Red));
Items^.ForEach(@DrawItem);
DeleteObject(SelectObject(DC, GetStockObject(Black_Brush)));
end;
{ Adjust the scale vertically }
procedure TVBarChart.AdjustScale(max : Integer);
begin
scale.x := 30;
scale.y := (area.y - 2 * lead.y) div max;
end;
{ Return item found at location x, y }
function TVBarChart.getItem(x, y : integer) : PAssoc;
var index : Integer;
begin
index := trunc((x - lead.x)/ (scale.x + space));
if index < items^.size then
getItem := Items^.at(index)
else
getItem := nil;
end;
{ Reset the lead for this type of chart }
procedure TVBarChart.resetLead;
begin
lead.x := 10;
lead.y := 30;
end;
{ Reset the space for this type of chart }
procedure TVBarChart.ResetSpace;
begin
Space := 30;
end;
{ ********* V3DBarChart *********}
{ Draw each 3D bar as a vertical bar, side and top polygons }
procedure TV3DBarChart.DrawData(DC : HDC);
var I, x, y : Integer;
procedure DrawItem(Item : PAssoc); far;
var points : array[1..4] of TPoint;
begin
x := Lead.x + i*(Scale.x + space);
y := area.y-lead.y-Item^.value*scale.y;
{ regular vertical bar }
Rectangle(DC, x+Scale.x, area.y - lead.y, x, y);
{ right side }
points[1].x := x+Scale.x - 1 ;
points[1].y := area.y - lead.y - 1;
points[2].x := x+Scale.x + space div 2 - 1;
points[2].y := area.y - lead.y - space div 2 - 1;
points[3].x := points[2].x;
points[3].y := y - space div 2;
points[4].x := x+Scale.x - 1;
points[4].y := y;
Polygon(DC, points, 4);
{ top }
points[1].x := x;
points[1].y := points[4].y;
points[2].x := x + space div 2;
points[2].y := points[3].y;
Polygon(DC, points, 4);
inc(i);
end;
begin
i := 0;
SelectObject(DC, CreateSolidBrush(Green));
Items^.ForEach(@DrawItem);
DeleteObject(SelectObject(DC, GetStockObject(Black_Brush)));
end;
{ ********* TPieChart ********* }
const
{ This table is used to cycle through RGB values of 0,
128, 255 for each color. This provides 27 patterns,
of which normally any consecutive 10 are unique. }
colors : array[0..2] of byte = (0, 128, 255);
{ Draw the labels and legends using a custom logical font }
procedure TPieChart.DrawLabels(DC : HDC);
var I, x, y : Integer;
s : PChar;
newFont, oldFont : hFont;
FontInfo : TLogFont;
procedure DrawLabel(Item : PAssoc); far;
var color : integer;
begin
y := lead.y + i * space;
s := Item^.key;
TextOut(DC, x, y, s, strLen(s));
{$R- can cause a range error }
color := RGB(colors[I mod 3],
colors[(I div 3) mod 3],
colors[(I div 9) mod 3]);
{$R+ can cause a range error }
SelectObject(DC, CreateSolidBrush(color));
Rectangle(DC, x + 60, y, x + 90, y + space div 2);
DeleteObject(SelectObject(DC, GetStockObject(Black_Brush)));
inc(i);
end;
begin
{ Create a logical font and select it }
with FontInfo do
begin
lfHeight := 18;
lfWidth := 0;
lfWeight := 700;
lfUnderLine := 0;
lfStrikeOut := 0;
lfItalic := 0;
strPcopy(lfFaceName, 'Tms Rmn');
end;
newFont := createFontIndirect(FontInfo);
OldFont := SelectObject(DC, newFont);
x := scale.x + space;
i := 0;
Items^.ForEach(@DrawLabel);
{ Reset the font when done }
selectObject(DC, oldFont);
DeleteObject(newFont);
end;
const TWO_PI = Pi * 2.0;
{ Draw the wedges in the pie }
procedure TPieChart.DrawData(DC : HDC);
var i, x, y, total : Integer;
nsum : array [0..26] of Integer;
{ Accumulate running total for Pies }
procedure addItems(Item : PAssoc); far;
begin
nsum[i+1] := nsum[i] + Item^.Value;
inc(i);
end;
procedure DrawItem(Item : PAssoc); far;
var color : Integer;
begin
{$R- can cause a range error }
color := RGB(colors[I mod 3],
colors[(I div 3) mod 3],
colors[(I div 9) mod 3]);
{$R+ can cause a range error }
SelectObject(DC, CreateSolidBrush(color));
Pie(DC, lead.x, lead.y,
scale.x+lead.x, scale.y+lead.y,
round(((x*cos(TWO_PI*nSum[i+1]/total)))+x)+lead.x,
round(((y*sin(TWO_PI*nSum[i+1]/total)))+y)+lead.y,
round(((x*cos(TWO_PI*nSum[i]/total)))+x)+lead.x,
round(((y*sin(TWO_PI*nSum[i]/total)))+y)+lead.y);
DeleteObject(SelectObject(DC, GetStockObject(Black_Brush)));
inc(i);
end;
begin
nsum[0] := 0;
i := 0;
Items^.ForEach(@AddItems);
total := nsum[items^.size];
x := scale.x div 2;
y := scale.y div 2;
i := 0;
Items^.ForEach(@DrawItem);
end;
{ Adjust the scale horizontally }
procedure TPieChart.AdjustScale(max : Integer);
begin
scale.x := round(0.95 *(area.y - lead.y));
scale.y := scale.x;
end;
{ Return item found at legend location x, y }
function TPieChart.getItem(x, y : integer) : PAssoc;
var index : Integer;
begin
index := trunc((y - lead.y)/ (space));
if (index < items^.size) and (x >= scale.x + space) then
getItem := Items^.at(index)
else
getItem := nil;
end;
{ Adjust the space for this type of chart }
procedure TPieChart.resetSpace;
begin
space := area.y div 7;
end;
{ Stream Registration records for each chart type }
const
RChart: TStreamRec = (
ObjType: 1002;
VmtLink: Ofs(TypeOf(TChart)^);
Load: @TChart.load;
Store: @TChart.store);
RHBarChart: TStreamRec = (
ObjType: 1003;
VmtLink: Ofs(TypeOf(THBarChart)^);
Load: @THBarChart.load;
Store: @THBarChart.store);
RVBarChart: TStreamRec = (
ObjType: 1004;
VmtLink: Ofs(TypeOf(TVBarChart)^);
Load: @TVBarChart.load;
Store: @TVBarChart.store);
RV3DBarChart: TStreamRec = (
ObjType: 1005;
VmtLink: Ofs(TypeOf(TV3DBarChart)^);
Load: @TV3DBarChart.load;
Store: @TV3DBarChart.store);
RPieChart: TStreamRec = (
ObjType: 1006;
VmtLink: Ofs(TypeOf(TPieChart)^);
Load: @TPieChart.load;
Store: @TPieChart.store);
{ Initialization }
begin
RegisterType(RChart);
RegisterType(RHBarChart);
RegisterType(RVBarChart);
RegisterType(RV3DBarChart);
RegisterType(RPieChart);
end.